#pragma once

#include "znx_proc.h"
#include "znx_event.h"
#include "znx_listener.h"
#include "comm/znx_buf.h"
#include "comm/znx_inet.h"
#include "comm/znx_pool.h"
#include "comm/znx_syscall.h"

typedef enum {
    ZNX_TCP_CONN,
    ZNX_UDP_CONN,
    ZNX_NOTIFY_CONN,    // epoll notify
    ZNX_FAKE_CONN,
} znx_conn_type_t;


typedef enum {
    ZNX_CONN_EPOLL_LT,
    ZNX_CONN_EPOLL_ET,
} znx_conn_flag_t;


typedef struct znx_conn_s           znx_conn_t;
typedef struct znx_ssl_conn_s       znx_ssl_conn_t;
typedef void (*znx_conn_handle_pt)(znx_conn_t *conn);


typedef struct {
    znx_result_t (*read)(znx_conn_t *conn, void *buf, size_t count);
    znx_result_t (*readv)(znx_conn_t *conn, znx_iovec_t *vector, int iovcnt);
    znx_result_t (*write)(znx_conn_t *conn, void *buf, size_t count);
    znx_result_t (*writev)(znx_conn_t *conn, znx_iovec_t *vector, int iovcnt);
} znx_conn_operator_t;


struct znx_conn_s {
    void                        *data;
    int                         fd;
    znx_event_t                 read;
    znx_event_t                 write;

    // net conn or h2/h3 fake conn, etc..
    znx_conn_type_t             type;
    znx_conn_flag_t             flag;

    znx_epoll_event_t           ee;

    znx_ssl_conn_t              *ssl;

    znx_listener_t              *l;

    znx_cleanup_list_t          cleanup_list;

    znx_address_t               *local_address;
    znx_address_t               *peer_address;
    znx_str_t                   local_address_text;
    znx_str_t                   peer_address_text;

    size_t                      recv_bytes;
    size_t                      sent_bytes;
    size_t                      requests;

    znx_conn_operator_t         *operator;

    // io start or stop.
    unsigned                    start:1;

    // for udp socket.
    unsigned                    shared:1;

    // for safe shutdown.
    unsigned                    server:1;
    unsigned                    client:1;

    unsigned                    timedout:1;
    unsigned                    closed:1;
    unsigned                    inited:1;
};


static inline void
znx_conn_init(znx_conn_t *conn,
    znx_conn_type_t type,
    znx_conn_flag_t flag)
{
    // it's a read event.
    conn->read.data = conn;
    conn->read.write = 0;

    // it's a write event.
    conn->write.data = conn;
    conn->write.write = 1;

    // epoll event
    conn->type = type;
    conn->flag = flag;
    conn->ee.data.ptr = conn;
    conn->fd = INVALID_FD;

    znx_cleanup_list_init(&conn->cleanup_list);

    conn->inited = 1;
}


static inline void
znx_conn_close(znx_conn_t *conn)
{
    conn->closed = 1;

    if (conn->ssl) {
        ZNX_ASSERT_FATAL("ssl conn no close");
    }

    if (conn->l) {
        znx_listener_conn_free(conn->l);
        conn->l = NULL;
    }

    if (conn->server) {
        znx_atomic_int64_add_fetch(&server_conns, -1);
        conn->server = 0;
    }

    if (conn->client) {
        znx_atomic_int64_add_fetch(&client_conns, -1);
        conn->client = 0;
    }

    znx_cleanup_list_exec(&conn->cleanup_list);

    if (conn->shared) {
        conn->fd = INVALID_FD;
        return;
    }

    if (conn->fd == INVALID_FD) {
        return;
    }

    znx_syscall_close(conn->fd);
    conn->fd = INVALID_FD;
}


static inline void
znx_conn_close_and_free(znx_conn_t *conn)
{
    if (conn->closed == 0) {
        znx_conn_close(conn);
    }
    znx_thread_free(conn);
}


znx_address_t *znx_conn_local_address(znx_conn_t *conn);
znx_address_t *znx_conn_peer_address(znx_conn_t *conn);
znx_str_t *znx_conn_local_address_text(znx_conn_t *conn);
znx_str_t *znx_conn_peer_address_text(znx_conn_t *conn);

znx_result_t znx_conn_read(znx_conn_t *conn, void *buf, size_t count);
znx_result_t znx_conn_readv(znx_conn_t *conn, znx_iovec_t *vector, int iovcnt);
znx_result_t znx_conn_write(znx_conn_t *conn, void *buf, size_t count);
znx_result_t znx_conn_writev(znx_conn_t *conn, znx_iovec_t *vector, int iovcnt);

znx_result_t znx_conn_udp_read(znx_conn_t *conn, void *buf, size_t count);
znx_result_t znx_conn_udp_readv(znx_conn_t *conn, znx_iovec_t *vector, int iovcnt);
znx_result_t znx_conn_udp_write(znx_conn_t *conn, void *buf, size_t count);
znx_result_t znx_conn_udp_writev(znx_conn_t *conn, znx_iovec_t *vector, int iovcnt);


extern znx_conn_operator_t  tcp_conn_operator;
extern znx_conn_operator_t  udp_conn_operator;
